/*
 * Copyright 2007-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.paoding.rose.web;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.paoding.rose.Rc;
import net.paoding.rose.web.impl.thread.ActionInvocation;
import net.paoding.rose.web.impl.thread.MatchResult;
import net.paoding.rose.web.var.Flash;
import net.paoding.rose.web.var.Model;
import net.paoding.rose.web.var.PrivateVar;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 
 * @author qieqie.wang@paoding.net
 * 
 */
public class InvocationBean implements Invocation {
	private static final Log logger = LogFactory.getLog(InvocationBean.class);

	private ActionInvocation actionInvocation;

	private Object[] methodParameters;

	private Map<String, Object> attributes;

	private Model model;

	private HttpServletRequest request;

	private HttpServletResponse response;

	private Flash flash;

	private MatchResult controllerMatchResult;
	private MatchResult actionMatchResult;
	

	public InvocationBean(ActionInvocation actionInvocation) {
		this.actionInvocation = actionInvocation;
		this.model = Rc.model();
		this.flash = Rc.flash();
		this.request = Rc.request();
		this.response = Rc.response();
		this.controllerMatchResult = PrivateVar.invocationVariables().getControllerMatchResult();
		this.actionMatchResult = PrivateVar.invocationVariables().getActionMatchResult();
	}

	public void setMethodParameters(Object[] methodParameters) {
		this.methodParameters = methodParameters;
	}

	@Override
	public Object getController() {
		return actionInvocation.getControllerInvocation().getController();
	}

	@Override
	public Class<?> getControllerClass() {
		return actionInvocation.getControllerInvocation().getControllerClass();
	}

	@Override
	public Method getMethod() {
		return actionInvocation.getMethod();
	}

	@Override
	public String[] getMethodParameterNames() {
		String[] copy = new String[actionInvocation.getParameterNames().length];
		System.arraycopy(actionInvocation.getParameterNames(), 0, copy, 0, copy.length);
		return copy;
	}

	@Override
	public Object[] getMethodParameters() {
		Object[] copy = new Object[methodParameters.length];
		System.arraycopy(methodParameters, 0, copy, 0, copy.length);
		return copy;
	}
	
	@Override
	public Object getMethodParameter(String name) {
		String[] names = actionInvocation.getParameterNames();
		for(int i = 0; i < names.length; i ++) {
			if (name != null && name.equals(names[i])) {
				return methodParameters[i];
			}
		}
		return null;
	}
	
	@Override
	public Object getParameter(String name) {
		Object value = getMethodParameter(name);
		if (value == null) {
			value = actionMatchResult.getParameter(name);
		}
		if (value == null) {
			value = controllerMatchResult.getParameter(name);
		}
		if (value == null) {
			value = flash.get(name);
		}
		if (value == null) {
			value = request.getParameter(name);
		}
		return value;
	}

	@Override
	public void changeMethodParameter(int index, Object value) {
		if (value != this.methodParameters[index]) {
			if (logger.isDebugEnabled()) {
				logger.debug("change method parameter "
						+ this.actionInvocation.getParameterNames()[index] + " (index=" + index
						+ ") from '" + this.methodParameters[index] + "' to '" + value + "'");
			}
			this.methodParameters[index] = value;
		}
	}

	public void changeMethodParameter(String name, Object value) {
		String[] names = this.actionInvocation.getParameterNames();
		for (int i = 0; i < names.length; i++) {
			if (name.equals(names[i])) {
				changeMethodParameter(i, value);
				return;
			}
		}
	}

	@Override
	// 不需要synchronized
	public Object getAttribute(String name) {
		if (attributes == null) {
			return null;
		}
		return attributes.get(name);
	}

	@Override
	public void removeAttribute(String name) {
		if (attributes != null) {
			attributes.remove(name);
		}
	}

	@Override
	public Set<String> getAttributeNames() {
		if (attributes == null) {
			return Collections.emptySet();
		}
		return attributes.keySet();
	}

	@Override
	public Invocation setAttribute(String name, Object value) {
		if (attributes == null) {
			attributes = new HashMap<String, Object>();
		}
		attributes.put(name, value);
		return this;
	}

	@Override
	public Model getModel() {
		return model;
	}

	@Override
	public Flash getFlash() {
		return flash;
	}

	@Override
	public HttpServletRequest getRequest() {
		return request;
	}

	@Override
	public HttpServletResponse getResponse() {
		return response;
	}

}
